{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. _networkset:\n",
    "| \n",
    "|\n",
    "Download This Notebook: :download:`NetworkSet.ipynb`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# NetworkSet\n",
    "\n",
    "\n",
    "## Introduction\n",
    "\n",
    "\n",
    "\n",
    "The [NetworkSet](../api/networkSet.rst) object represents an unordered  set of networks. It \n",
    "provides  methods iterating and slicing the set, sorting by datetime, calculating statistical quantities, and displaying uncertainty bounds on plots. \n",
    "\n",
    "## Creating a [NetworkSet](../api/networkSet.rst)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lets take a look in the `data/` folder, there are some redundant measurements of a network called `ro`,  which is a *radiating open* waveguide. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "ls data/ro*\n",
    "\n",
    "-a----       14/02/2021     12:35           8031 ro,1.s1p\n",
    "-a----       14/02/2021     12:35           8030 ro,2.s1p\n",
    "-a----       14/02/2021     12:35           8031 ro,3.s1p\n",
    "-a----       14/02/2021     12:35          46592 ro_spreadsheet.xls\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "raw_mimetype": "text/markdown"
   },
   "source": [
    "The files `ro,1.s1p` , `ro,2.s1p`, ...  are redundant measurements on \n",
    "which we would like to calculate statistics using the [NetworkSet](../api/networkSet.rst)\n",
    "class.\n",
    "\n",
    "A [NetworkSet](../api/networkSet.rst) is created from a list or dict of \n",
    "[Network](../api/network.rst)'s. So first we need to load all of the \n",
    "touchstone files into `Networks`. This can be done quickly with \n",
    "`rf.read_all`,  The argument `contains` is used to load only files \n",
    "which match a given substring. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import skrf as rf\n",
    "\n",
    "rf.read_all(rf.data.pwd, contains='ro')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This can be passed directly to the [NetworkSet](../api/networkSet.rst) constructor, "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from skrf import NetworkSet\n",
    "\n",
    "ro_dict = rf.read_all(rf.data.pwd, contains='ro')\n",
    "ro_ns = NetworkSet(ro_dict, name='ro set')\n",
    "ro_ns"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A NetworkSet can also be constructed directly from:\n",
    " - a directory containing Touchstone files: `NetworkSet.from_dir()`,\n",
    " - a zipfile of touchstones files: `NetworkSet.from_zip()`, \n",
    " - a dictionnary of s-parameters: `NetworkSet.from_s_dict()`,\n",
    " - a (G)MDIF (.mdf) file: `NetworkSet.from_mdif()`,\n",
    " - a CITI (.cti) file: `NetworkSet.from_citi()`. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Accessing Network Methods \n",
    "The [Network](../api/network.rst) elements in a [NetworkSet](../api/networkSet.rst) can be accessed like the elements of list, "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ro_ns[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Most [Network](../api/network.rst) methods are also methods of \n",
    "[NetworkSet](../api/networkSet.rst). These methods are called on each \n",
    "[Network](../api/network.rst) element individually. For example to \n",
    "plot the log-magnitude of the s-parameters of each Network."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import skrf as rf\n",
    "\n",
    "rf.stylely()\n",
    "\n",
    "ro_ns.plot_s_db()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Statistical Properties\n",
    "\n",
    "\n",
    "Statistical quantities can be calculated by accessing \n",
    "properties of the NetworkSet. To calculate the complex \n",
    "average of the set, access the `mean_s` property"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ro_ns.mean_s"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    "    \n",
    ".. note:: \n",
    "\n",
    "    Because the statistical operator methods are generated upon initialization\n",
    "    their API is not explicitly documented in this manual. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "    \n",
    "The naming convention of the statistical operator properties are `NetworkSet.{function}_{parameter}`, where `function` is the name of the \n",
    "statistical function, and `parameter` is the Network parameter to operate \n",
    "on. These methods return a [Network](../api/network.rst) object, so they can be \n",
    "saved or plotted in the same way as you would with a Network.\n",
    "To plot the log-magnitude of the complex mean response "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ro_ns.mean_s.plot_s_db(label='ro')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or to plot the standard deviation of the complex s-parameters,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ro_ns.std_s.plot_s_re(y_label='Standard Deviations')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using these properties it is possible to calculate statistical quantities on the scalar \n",
    "components of the complex network parameters. To calculate the \n",
    "mean of the phase component,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ro_ns.mean_s_deg.plot_s_re()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Plotting Uncertainty Bounds\n",
    "\n",
    "\n",
    "Uncertainty bounds can be plotted through the methods "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ro_ns.plot_uncertainty_bounds_s_db()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ro_ns.plot_uncertainty_bounds_s_deg()"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {
    "raw_mimetype": "text/restructuredtext"
   },
   "source": [
    ".. note::\n",
    "\n",
    "    The uncertainty bounds plotted above are calculated  **after** \n",
    "    the complex number has been projected onto the specified scalar component.\n",
    "    Thus, the first plot represents uncertainty in the magnitude component **only**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "## Plotting Violin Plots\n",
    "\n",
    "Violin plots are used to get an idea of the distribution at a glance by displaying a probability density at each point,\n",
    "along with the minimum, maximum, and average values by default. Let's interpolate the frequencies to reduce clutter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ro_ns_interp = ro_ns.interpolate_frequency(rf.Frequency(500, 600, 15, \"GHz\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Violin plots are available for most common network parameters, except for those in the time domain. To plot the log-magnitude and phase of the s-parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ro_ns_interp.plot_violin(\"s_db\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ro_ns_interp.plot_violin(\"s_deg\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## Reading and Writing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To write all [Network](../api/network.rst)s of a [NetworkSet](../api/networkSet.rst) out to individual touchstones,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ro_ns.write_touchstone(dir='data/')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For temporary data storage, [NetworkSet](../api/networkSet.rst)s can be saved and read from disk \n",
    "using  the functions `rf.read` and `rf.write`\n",
    "\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "rf.write('ro set.ns', ro_ns)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ro_ns = rf.read('ro set.ns')\n",
    "ro_ns"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Export to Excel, csv, or html"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[NetworkSet](../api/networkSet.rst)s can also be exported to other filetypes. The format of the output; real/imag, mag/phase is adjustable, as is the output type; csv, excel, html. For example to export mag/phase for each network into an Excel spreadsheet for your boss[s]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ro_ns.write_spreadsheet('data/ro_spreadsheet.xls', form='db')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "More info on this can be found in the function, `skrf.io.general.network_2_spreadsheet`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Named Parameters\n",
    "If all the `Network` objects of a `NetworkSet` have a `params` property containing a dictionnary of the named parameters and values associated to each Network, it is possible to select the Networks corresponding to a subset of named parameters using the `.sel()` method. \n",
    "\n",
    "The following example illustrates this feature."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# dummy named parameters and values 'a', 'X' and 'c'\n",
    "import numpy as np\n",
    "\n",
    "params = [\n",
    "        {'a':0, 'X':10, 'c':'A'},\n",
    "        {'a':1, 'X':10, 'c':'A'},\n",
    "        {'a':2, 'X':10, 'c':'A'},\n",
    "        {'a':1, 'X':20, 'c':'A'},\n",
    "        {'a':0, 'X':20, 'c':'A'},\n",
    "        ]\n",
    "# create a NetworkSet made of dummy Networks, each define for set of parameters\n",
    "freq1 = rf.Frequency(75, 110, 101, 'ghz')\n",
    "rng = np.random.default_rng()\n",
    "ntwks_params = [rf.Network(frequency=freq1, s=rng.uniform(size=(len(freq1),2,2)),\n",
    "                               name=f'ntwk_{m}', comment=f'ntwk_{m}', params=params)\n",
    "                            for (m, params) in enumerate(params) ]\n",
    "ns = rf.NetworkSet(ntwks_params)\n",
    "print(ns)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Selecting the sub-NetworkSet matching scalar parameters can be made as:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ns.sel({'a': 1})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ns.sel({'a': 0, 'X': 10})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Selecting the sub-NetworkSet matching a range of parameters also works:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ns.sel({'a': 0, 'X': [10,20]})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ns.sel({'a': [0,1], 'X': [10,20]})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The various named parameter keys and values of the NetworkSet can be retrieved using the `dims` and `coords` properties: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ns.dims"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ns.coords"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Interpolating between the Networks of a NetworkSet\n",
    "It is possible to create new Networks interpolated from the Networks contained in a `NetworkSet`. If no `params` properties have been defined for each Network of the NetworkSet, the `interpolate_from_network()` method can be used to specify a interpolating parameter. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "param_x = [1, 2, 3]  # a parameter associated to each Network\n",
    "x0 = 1.5  # parameter value to interpolate for\n",
    "interp_ntwk = ro_ns.interpolate_from_network(param_x, x0)\n",
    "print(interp_ntwk)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "An illustrated example is given in the [Examples section](../examples/index.rst#networksets) of the documentation.\n",
    "\n",
    "It is also possible to interpolate using a named parameter when they have been defined: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Interpolated Network for a=1.2 within X=10 Networks:\n",
    "ns.interpolate_from_params('a', 1.2, {'X': 10})"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "celltoolbar": "Raw Cell Format",
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
